package com.example.javapoet.demo.intermediate;

import com.squareup.javapoet.*;

import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * JavaPoet中级示例 - 生成带有泛型的类型
 */
public class GenericTypeGeneration {

    public static void main(String[] args) {
        // 生成一个带泛型的类
        generateGenericClass();
        
        // 生成一个带多个泛型参数的类
        generateMultipleGenericParamsClass();
        
        // 生成一个带有泛型边界的类
        generateGenericWithBoundsClass();
        
        // 生成一个带有通配符泛型的类
        generateWildcardGenericClass();
    }

    /**
     * 生成一个带泛型的类
     */
    public static void generateGenericClass() {
        // 定义泛型类型变量
        TypeVariableName typeT = TypeVariableName.get("T");
        
        // 创建字段
        FieldSpec valueField = FieldSpec.builder(typeT, "value")
                .addModifiers(Modifier.PRIVATE)
                .build();
        
        // 创建构造函数
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(typeT, "value")
                .addStatement("this.value = value")
                .build();
        
        // 创建getter和setter方法
        MethodSpec getValueMethod = MethodSpec.methodBuilder("getValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeT)
                .addStatement("return value")
                .build();
        
        MethodSpec setValueMethod = MethodSpec.methodBuilder("setValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addParameter(typeT, "value")
                .addStatement("this.value = value")
                .build();
        
        // 创建一个泛型方法
        MethodSpec mapMethod = MethodSpec.methodBuilder("map")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(TypeVariableName.get("R"))
                .returns(ParameterizedTypeName.get(
                        ClassName.get("com.example.generic", "Box"),
                        TypeVariableName.get("R")))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        typeT,
                        TypeVariableName.get("R")), "mapper")
                .addStatement("$T mappedValue = mapper.apply(value)", TypeVariableName.get("R"))
                .addStatement("return new $T<>(mappedValue)", ClassName.get("com.example.generic", "Box"))
                .build();
        
        // 创建类
        TypeSpec boxClass = TypeSpec.classBuilder("Box")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(typeT)
                .addField(valueField)
                .addMethod(constructor)
                .addMethod(getValueMethod)
                .addMethod(setValueMethod)
                .addMethod(mapMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.generic", boxClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成Box<T>类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成Box.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成一个带多个泛型参数的类
     */
    public static void generateMultipleGenericParamsClass() {
        // 定义泛型类型变量
        TypeVariableName typeK = TypeVariableName.get("K");
        TypeVariableName typeV = TypeVariableName.get("V");
        
        // 创建字段
        FieldSpec keyField = FieldSpec.builder(typeK, "key")
                .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
                .build();
        
        FieldSpec valueField = FieldSpec.builder(typeV, "value")
                .addModifiers(Modifier.PRIVATE)
                .build();
        
        // 创建构造函数
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(typeK, "key")
                .addParameter(typeV, "value")
                .addStatement("this.key = key")
                .addStatement("this.value = value")
                .build();
        
        // 创建getter和setter方法
        MethodSpec getKeyMethod = MethodSpec.methodBuilder("getKey")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeK)
                .addStatement("return key")
                .build();
        
        MethodSpec getValueMethod = MethodSpec.methodBuilder("getValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeV)
                .addStatement("return value")
                .build();
        
        MethodSpec setValueMethod = MethodSpec.methodBuilder("setValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeV)
                .addParameter(typeV, "value")
                .addStatement("$T oldValue = this.value", typeV)
                .addStatement("this.value = value")
                .addStatement("return oldValue")
                .build();
        
        // 创建一个静态工厂方法
        MethodSpec ofMethod = MethodSpec.methodBuilder("of")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(typeK)
                .addTypeVariable(typeV)
                .returns(ParameterizedTypeName.get(
                        ClassName.get("com.example.generic", "Pair"),
                        typeK, typeV))
                .addParameter(typeK, "key")
                .addParameter(typeV, "value")
                .addStatement("return new $T<>(key, value)", ClassName.get("com.example.generic", "Pair"))
                .build();
        
        // 创建类
        TypeSpec pairClass = TypeSpec.classBuilder("Pair")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(typeK)
                .addTypeVariable(typeV)
                .addField(keyField)
                .addField(valueField)
                .addMethod(constructor)
                .addMethod(getKeyMethod)
                .addMethod(getValueMethod)
                .addMethod(setValueMethod)
                .addMethod(ofMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.generic", pairClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成Pair<K,V>类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成Pair.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成一个带有泛型边界的类
     */
    public static void generateGenericWithBoundsClass() {
        // 定义带边界的泛型类型变量
        TypeVariableName typeT = TypeVariableName.get("T", ClassName.get(Number.class));
        TypeVariableName typeR = TypeVariableName.get("R", 
                ClassName.get(Comparable.class), ClassName.get(Cloneable.class));
        
        // 创建字段
        FieldSpec valueField = FieldSpec.builder(typeT, "value")
                .addModifiers(Modifier.PRIVATE)
                .build();
        
        // 创建构造函数
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(typeT, "value")
                .addStatement("this.value = value")
                .build();
        
        // 创建方法
        MethodSpec getValueMethod = MethodSpec.methodBuilder("getValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeT)
                .addStatement("return value")
                .build();
        
        MethodSpec doubleValueMethod = MethodSpec.methodBuilder("doubleValue")
                .addModifiers(Modifier.PUBLIC)
                .returns(double.class)
                .addStatement("return value.doubleValue()")
                .build();
        
        // 创建一个带边界的泛型方法
        MethodSpec convertMethod = MethodSpec.methodBuilder("convertTo")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(typeR)
                .returns(typeR)
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Function.class),
                        typeT,
                        typeR), "converter")
                .addStatement("return converter.apply(value)")
                .build();
        
        // 创建类
        TypeSpec numericBoxClass = TypeSpec.classBuilder("NumericBox")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(typeT)
                .addField(valueField)
                .addMethod(constructor)
                .addMethod(getValueMethod)
                .addMethod(doubleValueMethod)
                .addMethod(convertMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.generic", numericBoxClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成NumericBox<T extends Number>类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成NumericBox.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成一个带有通配符泛型的类
     */
    public static void generateWildcardGenericClass() {
        // 定义泛型类型变量
        TypeVariableName typeT = TypeVariableName.get("T");
        
        // 创建字段
        ParameterizedTypeName listOfT = ParameterizedTypeName.get(
                ClassName.get(List.class), typeT);
        
        FieldSpec itemsField = FieldSpec.builder(listOfT, "items")
                .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
                .initializer("new $T<>()", ArrayList.class)
                .build();
        
        // 创建构造函数
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .build();
        
        // 创建方法
        MethodSpec addMethod = MethodSpec.methodBuilder("add")
                .addModifiers(Modifier.PUBLIC)
                .returns(boolean.class)
                .addParameter(typeT, "item")
                .addStatement("return items.add(item)")
                .build();
        
        MethodSpec getMethod = MethodSpec.methodBuilder("get")
                .addModifiers(Modifier.PUBLIC)
                .returns(typeT)
                .addParameter(int.class, "index")
                .addStatement("return items.get(index)")
                .build();
        
        // 使用通配符泛型
        ParameterizedTypeName wildcardCollection = ParameterizedTypeName.get(
                ClassName.get(Collection.class),
                WildcardTypeName.subtypeOf(typeT));
        
        MethodSpec addAllMethod = MethodSpec.methodBuilder("addAll")
                .addModifiers(Modifier.PUBLIC)
                .returns(boolean.class)
                .addParameter(wildcardCollection, "collection")
                .addStatement("return items.addAll(collection)")
                .build();
        
        // 使用另一种通配符泛型
        ParameterizedTypeName consumerWildcard = ParameterizedTypeName.get(
                ClassName.get("java.util.function", "Consumer"),
                WildcardTypeName.supertypeOf(typeT));
        
        MethodSpec forEachMethod = MethodSpec.methodBuilder("forEach")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addParameter(consumerWildcard, "consumer")
                .beginControlFlow("for ($T item : items)", typeT)
                .addStatement("consumer.accept(item)")
                .endControlFlow()
                .build();
        
        // 创建一个带通配符的静态方法
        MethodSpec copyMethod = MethodSpec.methodBuilder("copy")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariable(typeT)
                .returns(ParameterizedTypeName.get(
                        ClassName.get("com.example.generic", "Container"),
                        typeT))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Collection.class),
                        WildcardTypeName.subtypeOf(typeT)), "source")
                .addStatement("$T<$T> result = new $T<>()", 
                        ClassName.get("com.example.generic", "Container"),
                        typeT,
                        ClassName.get("com.example.generic", "Container"))
                .addStatement("result.addAll(source)")
                .addStatement("return result")
                .build();
        
        // 创建类
        TypeSpec containerClass = TypeSpec.classBuilder("Container")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(typeT)
                .addField(itemsField)
                .addMethod(constructor)
                .addMethod(addMethod)
                .addMethod(getMethod)
                .addMethod(addAllMethod)
                .addMethod(forEachMethod)
                .addMethod(copyMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.generic", containerClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成Container<T>类（带通配符泛型）:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成Container.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}